// tu_mutex.cpp	-- Vitaly Alexeev <tishka92@yahoo.com>	2009

// This source code has been donated to the Public Domain.  Do
// whatever you want with it.

#include "gameswf_mutex.h"

#define DEF_CREATE_THREAD_FUNC(type)	static tu_thread* createThread(thread_proc fn, void* data) { return new tu_thread_##type(fn, data); }
#define DEF_CREATE_MUTEX_FUNC(type)		static tu_mutex* createMutex() { return new tu_mutex_##type(); }
#define DEF_CREATE_COND_FUNC(type)		static tu_condition* createCondition() { return new tu_condition_##type(); }

#if TU_CONFIG_LINK_TO_THREAD == 1

#include <SDL.h>

namespace gameswf
{
	int sdl_thread_start_func(void* ptr)
	{
		((tu_thread*) ptr)->start();
		return 0;
	};

	class tu_thread_sdl : public tu_thread {
	public:
		tu_thread_sdl() { assert(false); }
		tu_thread_sdl(thread_proc fn, void* data)
		{
			IF_VERBOSE_ACTION(log_msg("SDL thread is started\n"));
			m_func = fn;
			m_arg = data;
			m_thread = SDL_CreateThread(sdl_thread_start_func, this);
			assert(m_thread);
		}

		~tu_thread_sdl()
		{
			kill();
		}

		void wait()
		{
			SDL_WaitThread(m_thread, NULL);
			m_thread = NULL;
		}

		void kill()
		{
			SDL_KillThread(m_thread);
			m_thread = NULL;
		}

		void start()
		{
			(m_func)(m_arg);
		}

	protected:
		SDL_Thread* m_thread;
		thread_start_func m_func;
		void* m_arg;
	};

	class tu_mutex_sdl : public tu_mutex {
	public:
		tu_mutex_sdl()
		{
			m_mutex = SDL_CreateMutex();
		}

		~tu_mutex_sdl() 
		{
			SDL_DestroyMutex(m_mutex);
		}

		void lock() 
		{
			SDL_LockMutex(m_mutex);
		}

		void unlock() 
		{
			SDL_UnlockMutex(m_mutex);
		}

	protected:
		SDL_mutex* m_mutex;
	};

	class tu_condition_sdl : public tu_condition {
	public:
		tu_condition_sdl()
		{
			m_cond = SDL_CreateCond();
		}

		~tu_condition_sdl()
		{
			SDL_DestroyCond(m_cond);
		}

		void wait()
		{
			m_cond_mutex.lock();
			SDL_CondWait(m_cond, m_cond_mutex.m_mutex);
		}

		void signal()
		{
			SDL_CondSignal(m_cond);
		}

	protected:
		SDL_cond* m_cond;
		tu_mutex m_cond_mutex;
	};

	DEF_CREATE_THREAD_FUNC(sdl);
	DEF_CREATE_MUTEX_FUNC(sdl);
	DEF_CREATE_COND_FUNC(sdl);
}

#elif TU_CONFIG_LINK_TO_THREAD == 2	// libpthread

#include <pthread.h>

namespace gameswf
{
	void* pthread_start_func(void* ptr)
	{
		((tu_thread*) ptr)->start();
		pthread_exit(NULL);
		return NULL;
	};

	class tu_thread_pthread : public tu_thread {
	public:
		tu_thread_pthread() { assert(false); }
		tu_thread_pthread(thread_proc fn, void* data)
		{
			IF_VERBOSE_ACTION(log_msg("pthread is started\n"));
			m_func = fn;
			m_arg = data;
			if (pthread_create(&m_thread, NULL, pthread_start_func, this))
			{
				log_msg("Couldn't create the pthread\n");
			}
		}

		~tu_thread_pthread()
		{
			kill();
		}

		void wait()
		{
			// blocks the calling thread until the specified threadid thread terminates. 
			pthread_join(m_thread, NULL);
		}

		void kill()
		{
			pthread_cancel(m_thread);
		}

		void start()
		{
			(m_func)(m_arg);
		}

	protected:
		pthread_t m_thread;
		thread_start_func m_func;
		void* m_arg;
	};

	class tu_mutex_pthread : public tu_mutex {
	public:
		tu_mutex_pthread()
		{
			pthread_mutex_init(&m_mutex, NULL);
		}

		~tu_mutex_pthread() 
		{
			pthread_mutex_destroy(&m_mutex);
		}

		void lock() 
		{
			pthread_mutex_lock(&m_mutex);
		}

		void unlock() 
		{
			pthread_mutex_unlock(&m_mutex);
		}

	protected:
		pthread_mutex_t m_mutex;
	};

	class tu_condition_pthread : public tu_condition {
	public:
		tu_condition_pthread()
		{
			pthread_cond_init(&m_cond, NULL);
		}

		~tu_condition_pthread()
		{
			pthread_cond_destroy(&m_cond);
		}

		void wait()
		{
			m_cond_mutex.lock();
			pthread_cond_wait(&m_cond, &m_cond_mutex.m_mutex);
			m_cond_mutex.unlock();
		}

		void signal()
		{
			pthread_cond_signal(&m_cond);
		}

	protected:
		pthread_cond_t m_cond;
		tu_mutex m_cond_mutex;
	};

	DEF_CREATE_THREAD_FUNC(pthread);
	DEF_CREATE_MUTEX_FUNC(pthread);
	DEF_CREATE_COND_FUNC(pthread);
}

#elif TU_CONFIG_LINK_TO_THREAD == 3	// for windows

#include <windows.h>

namespace gameswf {
	static DWORD WINAPI win32_thread_start_func(LPVOID ptr)
	{
		((tu_thread*) ptr)->start();
		return 0;
	};

	class tu_thread_win32 : public tu_thread {
	public:
		tu_thread_win32() { assert(false); }
		tu_thread_win32(thread_proc fn, void* data)
		{
			IF_VERBOSE_ACTION(log_msg("WIN32 thread is started\n"));

			m_func = fn;
			m_arg = data;

			DWORD dwID = 0;
			m_thread = ::CreateThread(
							NULL,
							0,
							win32_thread_start_func,
							this,
							0,
							&dwID);
			assert(m_thread);
		}

		~tu_thread_win32()
		{
			kill();
		}

		void wait()
		{
			::WaitForSingleObject(m_thread, INFINITE);
		}

		void kill()
		{
			assert(m_thread);

			DWORD dwExitCode = 0;
			::GetExitCodeThread(m_thread, &dwExitCode);
			::TerminateThread(m_thread, dwExitCode);
			
			m_thread = NULL;
		}

		void start()
		{
			(m_func)(m_arg);
		}

	protected:
		HANDLE m_thread;
		thread_start_func m_func;
		void* m_arg;
	};

	class tu_mutex_win32 : public tu_mutex {
	public:
		tu_mutex_win32()
		{
			::InitializeCriticalSection(&m_cs);
		}

		~tu_mutex_win32() 
		{
			::DeleteCriticalSection(&m_cs);
		}

		void lock() 
		{
			::EnterCriticalSection(&m_cs);
		}

		void unlock() 
		{
			::LeaveCriticalSection(&m_cs);
		}

	protected:
		CRITICAL_SECTION m_cs;
	};

	class tu_condition_win32 : public tu_condition {
	public:
		tu_condition_win32()
		{
			m_event = ::CreateEvent(NULL,FALSE,FALSE,NULL);
			assert(m_event);
		}

		~tu_condition_win32()
		{
			::CloseHandle(m_event);
		}

		void wait()
		{
			::WaitForSingleObject(m_event, INFINITE);
		}

		void signal()
		{
			::SetEvent(m_event);
		}

	protected:
		HANDLE m_event;
	};

	DEF_CREATE_THREAD_FUNC(win32);
	DEF_CREATE_MUTEX_FUNC(win32);
	DEF_CREATE_COND_FUNC(win32);
}	// namespace gameswf

#else

// no multithreads

#endif	// TU_CONFIG_LINK_TO_THREAD

namespace gameswf {
	static create_thread_func s_create_thread_func = createThread;
	static create_mutex_func s_create_mutex_func = createMutex;
	static create_condition_func s_create_condition_func = createCondition;

	void setCreateThreadFunc(create_thread_func func)
	{
		s_create_thread_func = func;
	}

	void setCreateMutexFunc(create_mutex_func func)
	{
		s_create_mutex_func = func;
	}

	void setCreateConditionFunc(create_condition_func func)
	{
		s_create_condition_func = func;
	}
}	// namespace gameswf
